package fun.tan90.device.protocol.config;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.StrUtil;
import fun.tan90.device.event.EventTopic;
import fun.tan90.device.event.model.DeviceSourceData;
import lombok.extern.slf4j.Slf4j;
import org.noear.dami.Dami;
import org.noear.liquor.eval.CodeSpec;
import org.noear.liquor.eval.ParamSpec;
import org.noear.liquor.eval.Scripts;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.bean.LifecycleBean;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Component
public class ProtocolJavaFactory implements LifecycleBean {

    private static final String CODE_SPEC_KEY = "{}_{}";

    private final static Map<String, CodeSpec> CODE_SPEC_MAP = new HashMap<>();

    @Override
    public void start() {
        ONode protocolInfoNode = Dami.<Pair<String, String>, ONode>bus().sendAndRequest(EventTopic.PROTOCOL_JA_INFO_TOPIC, null);
        log.info("ProtocolInfo: {}", protocolInfoNode.toJson());
        protocolInfoNode.ary().forEach(this::buildCodeSpec);
    }

    @Override
    public void postStart() {
        Dami.<DeviceSourceData, ONode>bus().listen(EventTopic.PROTOCOL_JA_EVAL_TOPIC, payload -> {
            DeviceSourceData deviceSourceData = payload.getContent();
            String deviceType = deviceSourceData.getDeviceType();
            String dataSource = deviceSourceData.getDataSource();
            CodeSpec codeSpec = CODE_SPEC_MAP.get(codeSpecKey(deviceType, dataSource));
            if (Objects.isNull(codeSpec)) {
                payload.reply(null);
                return;
            }
            String data = deviceSourceData.getData();
            Object res = Scripts.eval(codeSpec, Map.of("data", data));
            payload.reply(ONode.load(res));
        });
    }

    private String codeSpecKey(String deviceType, String dataSource) {
        return StrUtil.format(CODE_SPEC_KEY, deviceType, dataSource);
    }

    public void buildCodeSpec(ONode protocolNode) {
        String deviceType = protocolNode.get("deviceType").getString();
        String dataSource = protocolNode.get("dataSource").getString();
        String codeSpecStr = protocolNode.get("codeSpec").getString();
        ONode paramSpec = ONode.loadStr(protocolNode.get("paramSpec").getString());
        String returnType = protocolNode.get("returnType").getString();
        List<ParamSpec> parameters = paramSpec.ary().stream().map(param ->
                        new ParamSpec(param.get("paramName").getString(), ClassLoaderUtil.loadClass(param.get("paramClassName").getString())))
                .toList();
        CODE_SPEC_MAP.put(codeSpecKey(deviceType, dataSource), new CodeSpec(codeSpecStr)
                .parameters(parameters.toArray(new ParamSpec[0]))
                .returnType(ClassLoaderUtil.loadClass(returnType)));
    }
}
